var GenericMainWindow = GFMainWindow.extend({
	obj_id: null,
	ime: null,
	factory: null,
	constructor: function(ime,id,fixed)
	{
		var html;
		var x = this;

		this.ime = ime;

		html = '<ul>';
		html += lib.sprintf('<li id="%s_Logo" class="logo" title="%s">GownFull</li>',id,$GFS('GownFull IME (Click here to open main menu)'));
		html += lib.sprintf('<li id="%s_SelectIM" class="ime" title="%s">%s</li>',id,$GFS('Click here to select Input Method'),$GFS('Select Input Method'));
		html += lib.sprintf('<li id="%s_ArrowIM" class="arrow" title="%s">\u25bc</li>',id,$GFS('Click here to select Input Method'));
/*		html += lib.sprintf('<li id="%s_SelectOutput" class="ime" title="%s">%s</li>',id,$GFS('Click here to select Output Method'),$GFS(':output'));
		html += lib.sprintf('<li id="%s_ArrowOutput" class="arrow" title="%s">\u25bc</li>',id,$GFS('Click here to select Output Method'));
*/
//		html += lib.sprintf('<li id="%s_Extension">&nbsp;</li>',id);
		html += lib.sprintf('<li id="%s_Last" class="preedit"><span id="%s_Preedit" class="status">%s</span></li>',id,id,$GFS('Click "Select Input Method" to select Input Method'));
		html += '</ul>';
		html += '<br clear="all">';

		this.obj_id = id;
		if(fixed) this.base(new GFWindowFixedBehavior(id,'MainWnd'),html);
		else this.base(new GFWindowMovableBehavior(id,0,0,null,null,'MainWnd'),html);

		if(GownFullBrowserDetect.use_addEventListener) {
			var obj = this.GetSelect();

			obj.addEventListener('click',function(e) { return x.PopupSwitchIMMenu(this,e); },false);
			obj = this.GetLogo();
			obj.addEventListener('click',function(e) { return x.PopupMenu(this,e); }, false);
			obj = this.GetArrow();
			obj.addEventListener('click',function(e) { return x.PopupSwitchIMMenu(this,e); }, false);
		}
		else {
			var obj = this.GetSelect();

			obj.onclick = function() { return x.PopupSwitchIMMenu(this,window.event); };
			obj = this.GetLogo();
			obj.onclick = function() { return x.PopupMenu(this,window.event); };
			obj = this.GetArrow();
			obj.onclick = function() { return x.PopupSwitchIMMenu(this,window.event); };
		}
		// Get the ul obj
		var obj = document.getElementById(this.obj_id);
		var ul = obj.firstChild;
		var last = document.getElementById(this.obj_id + '_Last');
		this.factory = new GenericExtensionAreaFactory(ul,last);
	},
	GetSelect: function()
	{
		return document.getElementById(this.obj_id + '_SelectIM');
	},
	GetLogo: function()
	{
		return document.getElementById(this.obj_id + '_Logo');
	},
	GetArrow: function()
	{
		return document.getElementById(this.obj_id + '_ArrowIM');
	},
	PopupMenu: function(obj,e)
	{
		var x,y;

		this.ime.menu.HideMenu();
		var o = this.GetLogo();
		var pos = JSVKBrowserExtension.getOffset(o);

		x = pos.x;
		y = pos.y + pos.height;
		pos = GFWindowsMovableHandler.FixPosition(x,y);
		x = pos[0]; y = pos[1];

		this.ime.menu.ShowMenu();
		this.ime.candwin.SetWindowPosition(x,y);
		return true;
	},
	PopupSwitchIMMenu: function(obj,e)
	{
		var x,y;

		this.ime.menu.HideMenu();
		var o = this.GetSelect();
		var pos = JSVKBrowserExtension.getOffset(o);

		x = pos.x;
		y = pos.y + pos.height;
		pos = GFWindowsMovableHandler.FixPosition(x,y);
		x = pos[0]; y = pos[1];
		this.ime.menu.ShowIMMenu(true);
		this.ime.candwin.SetWindowPosition(x,y);
		return true;
	},
	GetExtensionFactory: function()
	{
		return this.factory;
	},
	UpdateIMName: function(name)
	{
		var obj;

		obj = this.GetSelect();
		obj.innerHTML = name;
	},
	GetPreedit: function()
	{
		var buf;
		
		buf = document.getElementById(this.obj_id + '_Preedit');
		if(buf) return buf;
		else return null;
	},
	SetPreedit: function(str)
	{
		var buf;
		
		buf = document.getElementById(this.obj_id + '_Preedit');
//		if(buf && buf.firstChild) buf.firstChild.nodeValue = str;
		buf.innerHTML = str;
	},
	ResetPreedit: function()
	{
		var buf;

		buf = this.GetPreedit();
		if(buf) {
			buf.className = '';
//			if(buf.firstChild) buf.firstChild.nodeValue = '';
			buf.innerHTML = '';
		}
	},
	SetPreeditText: function(str)
	{
		var buf;

		buf = this.GetPreedit();
		if(buf) {
			buf.className = 'status';
			//if(buf.firstChild) buf.firstChild.nodeValue = str;
			buf.innerHTML = str;
		}
	},
	DeletePreedit: function(c)
	{
		var buf;

		if(c == null) c = 1;

		buf = this.GetPreedit();
		if(buf) {
			if(buf.className && buf.className != GownFullConfig.preedit_class_name) this.ResetPreedit();
			var str = buf.innerHTML;
			str = str.substr(0,str.length - c);
			buf.innerHTML = str;
/*			if(buf.firstChild) {
				var str = buf.firstChild.nodeValue;
				str = str.substr(0,str.length - c);
				buf.firstChild.nodeValue = str;
			}
*/
		}
	},
	AppendPreedit: function(c)
	{
		var buf;

		buf = this.GetPreedit();
		if(buf) {
			if(buf.className) {
				if(buf.className != '') {
					 //if(buf.firstChild) buf.firstChild.nodeValue = '';
					buf.innerHTML = '';
				}
				buf.className = '';
			}
			buf.innerHTML += c;
//			if(buf.firstChild) buf.firstChild.nodeValue += c;
		}
	},
	SetPreeditInvalid: function()
	{
		var buf;

		buf = this.GetPreedit();
		if(buf) buf.className = 'invalid';
	},
	onSelectChange: function(sel)
	{
		this.selchg_handler(sel.value);
	}
});

var GenericNumberCandidateList = GFCandidateList.extend({
	win: null,

	cancel_handler: null,
	d_list: null,
	d_func: null,
	d_header: null,
	d_pos: 0,
	num_per_page: 9,

	constructor: function(name,display,modifiers,className)
	{
		this.name = name;
		this.display = display;
		this.modifiers = modifiers;
		this.className = className;
		this.keyset = 'Num';
	},
	CreateDIV: function(win,div)
	{
		var x = this;
		var html,child,i;
		var id;

		this.win = win;
		id = win.obj_id;
			
		html = '';
		child = document.createElement('div');
		child.id = win.obj_id + '_' + this.name + '_DIV';
		child.className = this.className;
		child.style.display = 'none';

		if(this.display) {
			html += '<div class="header">';
			html += lib.sprintf('<span class="position" id="%s_%s_position"></span>',id,this.name);
			html += lib.sprintf('<span class="title" id="%s_%s_header">%s</span>',id,this.name,this.name);
			html += lib.sprintf('<span class="prev button" id="%s_%s_prev" title="%s">&lt;</span>',id,this.name,$GFS('Previous Page'));
			html += lib.sprintf('<span class="next button" id="%s_%s_next" title="%s">&gt;</span>',id,this.name,$GFS('Next Page'));
			html += lib.sprintf('<span class="cancel button" id="%s_%s_cancel" title="%s">X</span>',id,this.name,$GFS('Cancel'));
			html += '</div>';
			html += '<br clear="both" />';
			html += '<div class="list">';
		
		}
		html += '<ol>';
		for(i=1;i<=this.num_per_page;i++) {
			html += lib.sprintf('<li id="%s_%s_Candidate%d">&nbsp;</li>',id,this.name,i);
		}
		html += '</ol>';

		if(this.display) html += '</div>';

		child.innerHTML = html;
		div.appendChild(child);

		// attach handler
		var obj;
		var buttonfun = function() {
			var tid = this.id;
			var parts = tid.split(/_/);

			switch(parts[2]) {
			case 'prev': return x.onPrevPage(null,parts[1]); break;
			case 'next': return x.onNextPage(null,parts[1]); break;
			case 'cancel': return x.onCancel(null,parts[1]); break;
			}
		}

		for(i=1;i<=this.num_per_page;i++) {
			obj = this.GetCandidateObject(i);
			obj.onclick = function() { return x._onClickHandler(this); };
		}
		if(this.display) {
			obj = this.GetSubObject('prev');
			obj.onclick = buttonfun;
			obj = this.GetSubObject('next');
			obj.onclick = buttonfun;
			obj = this.GetSubObject('cancel');
			obj.onclick = buttonfun;
		}
	},
	GetDIVObject: function()
	{
		return document.getElementById(this.win.obj_id + '_' + this.name + '_DIV');
	},
	GetSubObject: function(name)
	{
		return document.getElementById(this.win.obj_id + '_' + this.name + '_' + name);
	},
	GetCandidateObject: function(i)
	{
		return document.getElementById(this.win.obj_id + '_' + this.name + '_Candidate' + i);
	},
	SetCancelHandler: function(handler)
	{
		this.cancel_handler = handler;
	},
	SetHeader: function(header)
	{
		if(this.display) {
			var header = this.GetSubObject('header');
			header.innerHTML = header;
		}
	},
	SetList: function(header,list,selfunc)
	{
		this.d_header = header;
		this.d_list = list;
		this.d_header = header;
		this.d_func = selfunc;
		this.d_pos = 0;
	},
	GetList: function()
	{
		return this.d_list;
	},
	UpdateList: function(list)
	{
		this.d_list = list;
		if(list == null || this.d_pos >= this.d_list.length) this.d_pos = 0;
		return true;
	},
	AppendList: function(list)
	{
		if(this.d_list) {
			this.d_list.concat(list);
			return true;
		}
		else return false;
	},
	ResetList: function()
	{
		this.d_list = null;
		this.d_func = null;
		this.d_header = null;
		this.d_pos = 0;
		this.cancel_handler = null;
	},
	hasList: function()
	{
		return (this.d_list) ? true : false;
	},
	Show: function(show)
	{
		var o = this.GetDIVObject();

		if(show) o.style.display = '';
		else o.style.display = 'none';
	},
	_onClickHandler: function(li)
	{
		var regex = /^.*_.*_Candidate(\d)$/
		var parts;

		parts = regex.exec(li.id);
		if(parts) return this.onSelectCandidate(null,parseInt(parts[1]));
		else {
			Debug.trace(0,'GenericNumberCandidateList: unknown for id="%s"',li.id);
		}
	},
	onSelectCandidate: function(obj,choice)
	{
		if(this.d_list) {
			var pos = this.d_pos + choice - 1;
			if(pos < 0 || pos >= this.d_list.length) return false;
			if(this.d_func) {
				if(this.d_func(obj,pos)) this.onCancel(obj);
				return true;
			}
			else return false;
		}
		else return false;
	},
	onNextPage: function(obj)
	{
		var pos = this.d_pos + this.num_per_page;
		if(pos >= this.d_list.length) pos = 0;
		this.d_pos = pos;
		this.UpdateContent();
		return false;
	},
	onPrevPage: function(obj)
	{
		var pos = this.d_pos - this.num_per_page;
		if(pos < 0) pos = this.d_list.length - this.d_list.length%this.num_per_page;
		if(pos >= this.d_list.length) pos = 0;
		this.d_pos = pos;
		this.UpdateContent();
		return false;
	},
	onCancel: function(obj)
	{
		var b = true;

		if(this.cancel_handler) b = this.cancel_handler(obj);

		if(b) {
			this.occupied_by = null;
			this.ResetList();
			this.win.UpdateCandidate();
		}
	},
	GetPageNumberString: function(a,b)
	{
		var current,total;

		total = Math.floor(b / this.num_per_page);
		total++;
		current = Math.floor(a / this.num_per_page);
		current++;
		return lib.sprintf('%d/%d',current,total);
	},

	UpdateContent: function()
	{
		var obj;
		var i;

		if(this.d_list) {
			if(this.display) {
				var header = (this.d_header) ? this.d_header : this.display;
				obj = this.GetSubObject('header');
				obj.innerHTML = header;
				obj = this.GetSubObject('position');
				obj.innerHTML = this.GetPageNumberString(this.d_pos,this.d_list.length);
				if(this.d_list.length > this.num_per_page) {
					obj = this.GetSubObject('prev');
					obj.style.display = '';
					obj = this.GetSubObject('next');
					obj.style.display = '';
					obj = this.GetSubObject('position');
					obj.style.display = '';
				}
				else {
					obj = this.GetSubObject('prev');
					obj.style.display = 'none';
					obj = this.GetSubObject('next');
					obj.style.display = 'none';
					obj = this.GetSubObject('position');
					obj.style.display = 'none';
				}
			}
			for(i=0;i<9;i++) {
				var pos = this.d_pos + i;
				var str;

				if(pos >= this.d_list.length) str = '';
				else str = this.d_list[pos];

				obj = this.GetCandidateObject(i+1);
				if(str) {
					obj.innerHTML = str;
					obj.style.display = '';
				}
				else obj.style.display = 'none';
			}
			this.Show(true);
			return true;
		}
		else {
			this.Show(false);
			return false;
		}
	}
});

var GenericNumpadCandidateList = GenericNumberCandidateList.extend({
	constructor: function(name,display,modifiers,className)
	{
		this.base(name,display,modifiers,className);
		this.keyset = 'Numpad';
	}
});

var GenericQWERTCandidateList = GenericNumberCandidateList.extend({
	constructor: function(name,display,modifiers,className)
	{
		this.base(name,display,modifiers,className);
		this.keyset = 'QWERT';
	},
	CreateDIV: function(win,div)
	{
		var x = this;
		var html,child,i;
		var id;

		this.win = win;
		id = win.obj_id;
			
		html = '';
		child = document.createElement('div');
		child.id = win.obj_id + '_' + this.name + '_DIV';
		child.className = this.className;
		child.style.display = 'none';

		if(this.display) {
			html += '<div class="header">';
			html += lib.sprintf('<span class="position" id="%s_%s_position"></span>',id,this.name);
			html += lib.sprintf('<span class="title" id="%s_%s_header">%s</span>',id,this.name,this.name);
			html += lib.sprintf('<span class="prev button" id="%s_%s_prev">&lt;</span>',id,this.name);
			html += lib.sprintf('<span class="next button" id="%s_%s_next">&gt;</span>',id,this.name);
			html += lib.sprintf('<span class="cancel button" id="%s_%s_cancel">X</span>',id,this.name);
			html += '</div>';
			html += '<br clear="both" />';
			html += '<div class="list">';
		
		}
		html += '<ol>';
		html += lib.sprintf('<li value="17" id="%s_ShiftQWERT_Candidate1">&nbsp;</li>',id);
		html += lib.sprintf('<li value="23" id="%s_ShiftQWERT_Candidate2">&nbsp;</li>',id);
		html += lib.sprintf('<li value="5" id="%s_ShiftQWERT_Candidate3">&nbsp;</li>',id);
		html += lib.sprintf('<li value="18" id="%s_ShiftQWERT_Candidate4">&nbsp;</li>',id);
		html += lib.sprintf('<li value="20" id="%s_ShiftQWERT_Candidate5">&nbsp;</li>',id);
		html += lib.sprintf('<li value="25" id="%s_ShiftQWERT_Candidate6">&nbsp;</li>',id);
		html += lib.sprintf('<li value="21" id="%s_ShiftQWERT_Candidate7">&nbsp;</li>',id);
		html += lib.sprintf('<li value="9" id="%s_ShiftQWERT_Candidate8">&nbsp;</li>',id);
		html += lib.sprintf('<li value="15" id="%s_ShiftQWERT_Candidate9">&nbsp;</li>',id);
		html += '</ol>';

		if(this.display) html += '</div>';

		child.innerHTML = html;
		div.appendChild(child);

		// attach handler
		var obj;
		var buttonfun = function() {
			var tid = this.id;
			var parts = tid.split(/_/);

			switch(parts[2]) {
			case 'prev': return x.onPrevPage(null,parts[1]); break;
			case 'next': return x.onNextPage(null,parts[1]); break;
			case 'cancel': return x.onCancel(null,parts[1]); break;
			}
		}

		for(i=1;i<=this.num_per_page;i++) {
			obj = this.GetCandidateObject(i);
			obj.onclick = function() { return x._onClickHandler(this); };
		}
		if(this.display) {
			obj = this.GetSubObject('prev');
			obj.onclick = buttonfun;
			obj = this.GetSubObject('next');
			obj.onclick = buttonfun;
			obj = this.GetSubObject('cancel');
			obj.onclick = buttonfun;
		}
	}
});

var GenericCandidateListWindow = GFCandidateListWindow.extend({
	obj_id: null,
	status: null,
	block_num_keypress: false,
	block_QWERT_keypress: false,
	block_pn_keypress: false,
	nomove: false,

	constructor: function(id)
	{
		var html,i,key;
		var x = this;
		
		html = '';
		html += lib.sprintf('<div id="%s_Status" class="status">&nbsp;</div>',id);

		this.obj_id = id;
		this.base(new GFWindowMovableBehavior(id,Math.round(GFWindowsMovableHandler.GetClientWidth()/2),0,null,null,'CandidateListWnd'),html);

		this.UpdateCandidate();

		this.AddList(new GenericNumberCandidateList('Num',$GFS('Num'),0,'candidate'));
		this.AddList(new GenericNumberCandidateList('ShiftNum',$GFS('Shift + Num'),Const.KEY_SHIFT,'candidate'));
		this.AddList(new GenericNumpadCandidateList('Numpad',$GFS('Numpad'),0,'candidate'));
		this.AddList(new GenericNumberCandidateList('DropBox',null,0,'candidate'));
		this.AddList(new GenericQWERTCandidateList('ShiftQWERT',$GFS('Shift + QWERT'),Const.KEY_SHIFT,'candidate QWERT'));

		// add event handler to GownFull...
		// Create Input Method event handler
		this._internal_keyboard_observer = function(obj,type,key,modifiers,b)
		{
			if(!b) return false;
			switch(type) {
			case Const.KEY_TYPE_PRESS: return x.keypress_handler(obj,key,modifiers);
			case Const.KEY_TYPE_DOWN: return x.keydown_handler(obj,key,modifiers);
			default: return true;
			}
		};
		GownFull.instance.AddKeyboardObserver(this._internal_keyboard_observer);
	},
	GetDIV: function()
	{
		return document.getElementById(this.obj_id);
	},
	AddList: function(obj)
	{
		var div = this.GetDIV();

		obj.CreateDIV(this,div);
		this.base(obj);
	},
	GetStatusObject: function()
	{
		return document.getElementById(this.obj_id + '_Status');
	},
	SetStatus: function(str)
	{
		this.status = str;
	},
	ShowWindow: function(show)
	{
		this.base(show);
		if(show) {
			this.BringToTop();
			if(!this.nomove) {
				// Check if adapter exist for the last focus obj...
				var last_focus = GownFull.instance.state.last_focus_obj;
				if(last_focus) {
					var adapter;
					var pos;
		
					adapter = GownFullObjectAdapter.GetAdapter(last_focus);
					if(adapter) pos = adapter.GetCursorXY();
					else {
						Debug.trace(1,'GenericCandidateListWindow: ShowWindow: (last focus) object %s has no adapter or not supported',last_focus.nodeName);
					}

					if(!pos) pos = GownFull.instance.mainwin.GetWindowPosition();
					else {
						pos[0] -= GFWindowsMovableHandler.GetScrollX();
						pos[1] -= GFWindowsMovableHandler.GetScrollY();
					}	
					if(pos) this.SetWindowPosition(pos[0]+Const.CAND_OFFSET_X,pos[1]+Const.CAND_OFFSET_Y);
				}
				else {
					Debug.trace(2,'GenericCandidateListWindow: ShowWindow: no last focus object.');
				}
			}
			this.nomove = false;
		}
	},
	UpdateCandidate: function(nomove)
	{
		if(nomove) this.nomove = true;
		else this.nomove = false;
		for(var key in this.lists) {
			var obj = this.lists[key];
			obj.UpdateContent();
		}
		this.checkShow();
	},
	checkShow: function()
	{
		var c = 0;
		var obj;

		for(var key in this.lists) {
			obj = this.lists[key];
			if(obj.d_list) c++;
		}
		obj = this.GetStatusObject();
		if(this.status) {
			obj.innerHTML = this.status;
			obj.style.display = '';
			c++;
		}
		else {
			obj.innerHTML = '';
			obj.style.display = 'none';
		}

		Debug.trace(5,"GenericCandidateListWindow: DIV show number = %d",c);
		if(c > 0) this.ShowWindow(true);
		else this.ShowWindow(false);
	},
	keypress_handler: function(obj,key,modifiers)
	{
		var b;

//		if(!this.shown) return true;
		if(!this.block_num_keypress && !this.block_QWERT_keypress && !this.block_pn_keypress) return true;

		b = true;
		switch(key) {
		case 49: case 50: case 51: case 52: case 53:
		case 54: case 55: case 56: case 57:
			if(this.block_num_keypress) b = false;
			break;
		// Q-O
		case 79:
		case 73:
		case 85:
		case 89:
		case 84:
		case 82:
		case 69:
		case 87:
		case 81:
			if(this.block_QWERT_keypress) b = false;
			break;
		case 44: case 46: case 60: case 62:
			// must block set b = false....safari seems to be bad in the case, check later
			b = false; //if(this.block_pn_keypress) b = false;
			break;
		}
		this.block_num_keypress = false;
		this.block_QWERT_keypress = false;
		this.block_pn_keypress = false;
		return b;
	},
	keydown_handler: function(obj,key,modifiers)
	{
		var qdst=0, listkey;
		if(!this.shown) return true;

		switch(key) {
		case 49: case 50: case 51: case 52: case 53:
		case 54: case 55: case 56: case 57:
			for(listkey in this.lists) {
				var list = this.lists[listkey];
				if(!list.occupied_by) continue; // skip the one which is not used.
				if(list.keyset == 'Num' && modifiers == list.modifiers) {
					list.onSelectCandidate(obj,key-48);
					this.block_num_keypress = true;
					return false;
				}
			}
			// if nothing match... try to match the one with modifiers == 0 (so that to make GF the same as Windows IME)
/*			for(listkey in this.lists) {
				var list = this.lists[listkey];
				if(!list.occupied_by) continue; // skip the one which is not used.
				if(list.keyset == 'Num' && list.modifiers == 0) {
					list.onSelectCandidate(obj,key-48);
					this.block_pn_keypress = true;
					return false;
				}
			}*/
			break;
		case 97: case 98: case 99: case 100: case 101:
		case 102: case 103: case 104: case 105:
			for(listkey in this.lists) {
				var list = this.lists[listkey];
				if(!list.occupied_by) continue; // skip the one which is not used.
				if(list.keyset == 'Numpad' && modifiers == list.modifiers) {
					list.onSelectCandidate(obj,key-96);
					this.block_num_keypress = true;
					return false;
				}
			}
			break;
		// Q-O
		case 79: qdst++; // O
		case 73: qdst++;
		case 85: qdst++;
		case 89: qdst++;
		case 84: qdst++;
		case 82: qdst++;
		case 69: qdst++;
		case 87: qdst++;
		case 81: qdst++; // Q
			for(listkey in this.lists) {
				var list = this.lists[listkey];
				if(!list.occupied_by) continue; // skip the one which is not used.
				if(list.keyset == 'QWERT' && modifiers == list.modifiers) {
					list.onSelectCandidate(obj,qdst);
					this.block_QWERT_keypress = true;
					return false;
				}
			}
			break;
		case 188: // previous page
			this.block_pn_keypress = false;
			for(listkey in this.lists) {
				var list = this.lists[listkey];
				if(!list.occupied_by) continue; // skip the one which is not used.
				if(list.modifiers == modifiers) {
					list.onPrevPage(obj);
					this.block_pn_keypress = true;
					return false;
				}
			}
			// if nothing match... try to match the one with modifiers == 0 (so that to make GF the same as Windows IME)
			for(listkey in this.lists) {
				var list = this.lists[listkey];
				if(!list.occupied_by) continue; // skip the one which is not used.
				if(list.modifiers == 0) {
					list.onPrevPage(obj);
					this.block_pn_keypress = true;
					return false;
				}
			}
			break;
		case 190: // next page
			this.block_pn_keypress = false;
			for(listkey in this.lists) {
				var list = this.lists[listkey];
				if(!list.occupied_by) continue; // skip the one which is not used.
				if(list.modifiers == modifiers) {
					list.onNextPage(obj);
					this.block_pn_keypress = true;
					return false;
				}
			}
			// if nothing match... try to match the one with modifiers == 0 (so that to make GF the same as Windows IME)
			for(listkey in this.lists) {
				var list = this.lists[listkey];
				if(!list.occupied_by) continue; // skip the one which is not used.
				if(list.modifiers == 0) {
					list.onNextPage(obj);
					this.block_pn_keypress = true;
					return false;
				}
			}
			break;
		case 27: // escape
			// cancel all...
			for(listkey in this.lists) {
				var list = this.lists[listkey];
				if(!list.occupied_by) continue;
				list.onCancel(obj);
			}
			return false;
			break;
		}
		return true;
	}
});

var GenericBrowserWindow = GFBrowserWindow.extend({
	obj_id: null,
	width: 640,
	height: 480,
	constructor: function(id)
	{
		var html;
		var x = this;

		this.obj_id = id;

		html = '<div class="frame_header">';
		html += lib.sprintf('<h1 id="%s_title">BrowserWindow</h1>',id);
		html += '<div class="controls">';
		html += lib.sprintf('<span id="%s_opennew" class="opennew">O</span>',id);
		html += lib.sprintf('<span id="%s_close" class="close">X</span>',id);
		html += '</div>';
		html += '</div>';
		html += lib.sprintf('<iframe id="%s_iframe" src="about:blank" width="%d" height="%d"></iframe>',id,this.width,this.height);

		this.base(new GFWindowMovableBehavior(id,0,0,null,null,'BrowserWnd'),html);

		// get back the button and set handler
		var b = document.getElementById(this.obj_id + '_opennew');
		b.onclick = function() { x.handler_opennew(); }
		b = document.getElementById(this.obj_id + '_close');
		b.onclick = function() { x.handler_close(); }
	},
	GetIFrameObject: function()
	{
		return document.getElementById(this.obj_id + '_iframe');
	},
	GetTitleObject: function()
	{
		return document.getElementById(this.obj_id + '_title');
	},
	SetWidth: function(n)
	{
		var ifm = this.GetIFrameObject();

		ifm.width = n;
		this.width = n;
	},
	SetHeight: function(n)
	{
		var ifm = this.GetIFrameObject();

		ifm.height = n;
		this.height = n;
	},
	handler_opennew: function()
	{
		var ifr = this.GetIFrameObject();

		window.open(ifr.src,'_blank');
		this.OpenURL('about:blank');
		this.ShowWindow(false);
	},
	handler_close: function()
	{
		this.OpenURL('about:blank');
		this.ShowWindow(false);
	},
	OpenURL: function(url)
	{
		var ifr = this.GetIFrameObject();
		var w,h,x,y,cw,ch;

		ifr.src = url;
		Debug.trace(2,'GenericBrowserWindow: Open URL: %s',url);

		cw = GFWindowsMovableHandler.GetClientWidth();
		ch = GFWindowsMovableHandler.GetClientHeight();
		w = Math.round(cw/2);
		h = Math.round(ch*0.8);
		x = Math.round((cw - w)/2);
		y = 0; //Math.round((ch - h)/2);
		if(x < 0) x = 0;
		this.SetWidth(w);
		this.SetHeight(h);
		this.SetWindowPosition(x,y);
		this.ShowWindow(true);
		this.BringToTop();
	},
	SetTitle: function(str)
	{
		var t = this.GetTitleObject();

		t.innerHTML = str;
	}
});

var GenericTooltipWindow = GFTooltipWindow.extend({
	obj_id: null,

	constructor: function(id)
	{
		var html,obj;
		var x = this;

		this.obj_id = id;

		html = '<span id="' + id + '_title"></span>&nbsp;<span id="' + id + '_close">X</span>';
		html += '<div id="' + id + '_tip"></div>';

		this.base(new GFWindowMovableBehavior(id,0,0,null,null,'TooltipWnd'),html);

		obj = this.GetObject('close');
		obj.onclick = function() {
			x.ShowWindow(false);
		}
	},
	GetObject: function(type)
	{
		return document.getElementById(this.obj_id + '_' + type);
	},
	SetTitle: function(title)
	{
		var obj;
		obj = this.GetObject('title');
		obj.innerHTML = title;
	},
	SetTip: function(tip)
	{
		var obj;
		obj = this.GetObject('tip');
		obj.innerHTML = tip;
	},
	ShowWindow: function(show)
	{
		this.base(show);
		if(show) this.BringToTop();
	},
	MoveToCursor: function()
	{
		// Check if adapter exist for the last focus obj...
		var last_focus = GownFull.instance.state.last_focus_obj;
		if(last_focus) {
			var adapter;
			var pos;

			adapter = GownFullObjectAdapter.GetAdapter(last_focus);
			if(adapter) pos = adapter.GetCursorXY();
			else {
				Debug.trace(1,'GenericBubbleTooltipWindow: MoveToCursor: (last focus) object %s has no adapter or not supported',last_focus.nodeName);
			}
			if(pos) this.SetWindowPosition(pos[0],pos[1]);
		}
		else {
			Debug.trace(2,'GenericBubbleTooltipWindow: MoveToCursor: no last focus object.');
		}
	},
	MoveToObject: function(obj)
	{
		var pos,x,y;
		// we assume obj is HTMLElement here...
		pos = JSVKBrowserExtension.getOffset(obj);
		x = pos.x;
		y = pos.y + pos.height;
		pos = GFWindowsMovableHandler.FixPosition(x,y);
		x = pos[0]; y = pos[1];

		this.SetWindowPosition(x,y);
	}
});

var GenericMiniBufferWindow = GFMiniBufferWindow.extend({
	obj_id: null,
	onblur_handler: null,
	stop_onblur: true,

	constructor: function(id,count,ime)
	{
		var html;
		var x = this;

		id = id + '-' + count;
		this.obj_id = id;

		html = '<span id="' + id + '_title">Mini Buffer</span>';
		html += lib.sprintf('<span id="%s_warning" class="warning"></span><br />',id);
		html += lib.sprintf('<textarea id="%s_textarea" class="GFMiniBuffer" cols="40" rows="5"></textarea><br />',id);
		html += lib.sprintf('<span id="%s_instruction"></span>',id);

		this.base(ime,new GFWindowMovableBehavior(id,0,0,null,null,'MiniBufferWnd'),html);

		var ta = this.GetObject('textarea');

		if(GownFullBrowserDetect.use_key_addEventListener) {
			ta.addEventListener("keydown",function(e) { return x.keydown_handler(e); }, false);
		}
		else if(GownFullBrowserDetect.use_attachEvent) {
			ta.attachEvent("onkeydown", function(e) { return x.keydown_handler(e); });
		}
		else {
			if(ta.onkeydown) this.old_onkeydown = ta.onkeydown;
			ta.onkeydown = function() { 
				var b;

				b = true;
				if(x.old_onkeydown) b &= x.old_onkeydown(window.event);
				b &= x.keydown_handler(window.event);
				return b;
			}
		}
		// make sure to make an adapter to my textarea...
		// FIXME: how about onFocus, etc?
		new GownFullHTMLTextareaAdapter(ta);
		ta.onblur = function() { return x.onBlurTextarea(); };
	},
	GetObject: function(type)
	{
		return document.getElementById(this.obj_id + '_' + type);
	},
	SetTitle: function(title)
	{
		var obj = this.GetObject('title');
		obj.innerHTML = title;
	},
	SetInstruction: function(instruction)
	{
		var obj = this.GetObject('instruction');
		obj.innerHTML = instruction;
	},
	keydown_handler: function(e)
	{
		var key = e.which ? e.which : e.keyCode;
		var km = this.map_key;
		var m = 0;
		var i;

		if(e.altKey) m |= Const.KEY_ALT;
		else if(e.ctrlKey) m |= Const.KEY_CTRL;
		else if(e.shiftKey) m |= Const.KEY_SHIFT;

		for(i=0;i<km.length;i++) {
			var o = km[i];

			if(key == o.key && m == o.mod) {
				if(o.fun instanceof Function) {
					o.fun(this);
				}
				if(e.preventDefault) e.preventDefault();
				return false;
			}
		}
		return true;
	},
	onBlurTextarea: function()
	{
		if(!this.stop_onblur && this.onblur_handler) this.onblur_handler(this);
	},
	AppendBuffer: function(str)
	{
		var ta;
		var adapter;

		ta = this.GetObject('textarea');

		// the textarea should have textarea object...
		adapter = GownFullObjectAdapter.GetAdapter(ta);
		if(adapter) {
			adapter.SendString(null,str);
		}
		else { // MUST BE A BUG
			Debug.trace(0,'GenericMiniBufferWindow: (BUG) My TEXTAREA does not have adapter.');
		}
	},
	SetBuffer: function(str)
	{
		var ta;

		ta = this.GetObject('textarea');
		// we should have object adapter of that object
		var adapter = GownFullObjectAdapter.GetAdapter(ta);
		if(adapter) {
			ta.value = '';
			adapter.SendString(ta,str);
		}
		else {
			ta.value = str;
			Debug.trace(0,'GenericMiniBufferWindow: (BUG) My TEXTAREA does not have adapter.');
		}
	},
	GetBuffer: function()
	{
		var ta;

		ta = this.GetObject('textarea');
		return ta.value;
	},
	DeleteBuffer: function()
	{
		var ta;

		ta = this.GetObject('textarea');
		ta.value = '';
	},
	GetAdapter: function()
	{
		var ta;

		ta = this.GetObject('textarea');
		return  GownFullObjectAdapter.GetAdapter(ta);
	},
	Start: function(obj)
	{
		if(this.shown) return;

		var ta,pos;
		var adapter;

		if(obj) {
			adapter = GownFullObjectAdapter.GetAdapter(obj);
			if(adapter) {
				// get the cursor position of the object
				pos = adapter.GetCursorXY();
				this.SetWindowPosition(pos[0],pos[1]+30);
			}
			else {
				Debug.trace(0,'GenericMiniBufferWindow: Reference Object has no adapter.');
			}
		}

		this.ShowWindow(true);
		this.BringToTop();

		ta = this.GetObject('textarea');
		if(ta.focus) ta.focus();
		this.stop_onblur = false;
	},
	End: function()
	{
		this.stop_onblur = true;
		this.ShowWindow(false);
	}
});

var GenericMiniBufferManager = GFMiniBufferManager.extend({
	miniwin_id: null,
	constructor: function(ime,id) { this.base(ime); this.miniwin_id = id; },
	CreateMiniBuffer: function() { return new GenericMiniBufferWindow(this.miniwin_id,this.mini_buffers.length,this.ime); }
});

var GenericGownFullMenu = GownFullMenu.extend({
	available: false,
	escape_last: 0,
	current_node: null,
	current_objlist: null,
	target_list: 'Num',
	constructor: function(ime)
	{
		var x = this;

		this.base(ime);
		this.current_node = this.head;

		// add event handler to GownFull...
		// Create Input Method event handler
		this._internal_keyboard_observer = function(obj,type,key,modifiers,b)
		{
			switch(type) {
			case Const.KEY_TYPE_PRESS: return x.keypress_handler(obj,key,modifiers);
			case Const.KEY_TYPE_DOWN: return x.keydown_handler(obj,key,modifiers);
			default: return true;
			}
		};
		this.ime.AddKeyboardObserver(this._internal_keyboard_observer);
	},
	SetMenuItem: function(nomove)
	{
		var candwin = this.ime.candwin;
		var candlist;
		var node = this.current_node;
		var list;
		var x = this;
		var i;

		if(!node) return false;

		// occupy the list...
		candlist = candwin.OccupyList(this,this.target_list,true);

		list = new Array();
		this.current_objlist = new Array();
		for(i=0;i<node.items.length;i++) {
			var item = node.items[i];
	
			if(item) {
				if(item.show) list[i] = item.display;
				else list[i] = '';
				this.current_objlist[i] = item.obj;
			}
			else {
				list[i] = '';
				this.current_objlist[i] = null;
			}
		}
		candlist.SetList(node.title,list,function(obj,pos) { return x.ListSelectHandler(obj,pos); });
		candlist.SetCancelHandler(function(obj) { x.HideMenu(); return false; });

		if(nomove) candwin.UpdateCandidate(nomove);
		else candwin.UpdateCandidate();
	},
	ListSelectHandler: function(obj,pos)
	{
		var obj = this.current_objlist[pos];

		if(!obj) return false;

		if(obj instanceof Function) {
			if(!obj(pos)) return false;
			this.HideMenu();
			return true;
		}
		else {
			this.ime.candwin.ReleaseAll(); // so that onCancel will occur
			this.current_node = obj; // replace the node
			this.ShowMenu();
			return false;
		}
	},
	ShowMenu: function(nomove)
	{
		this.ime.candwin.ReleaseAll();

		Debug.trace(2,"GenericGownFullMenu: Show Menu");
		this.UpdateIM();
		this.ime.ResetPreedit();
		if(nomove) this.SetMenuItem(nomove);
		else this.SetMenuItem();
		this.available = true;
		return true;
	},
	HideMenu: function()
	{
		Debug.trace(2,"GenericGownFullMenu: Hide Menu");

		this.ime.candwin.ReleaseList(this,'Num');
		this.ime.candwin.ReleaseList(this,'DropBox');
		this.ime.candwin.UpdateCandidate();
		this.available = false;
		this.current_node = this.head;
		this.target_list = 'Num';
		return true;
	},
	PopupCustomMenu: function(node,dropbox)
	{
		if(dropbox) {
			this.HideMenu();
			this.target_list = 'DropBox';
			this.UpdateIM();
			this.current_node = node;
			this.ShowMenu(true);
		}
		else {
			this.HideMenu();
			this.target_list = 'Num';
			this.UpdateIM();
			this.current_node = node;
			this.ShowMenu(true);
		}
	},
	ShowIMMenu: function(mini)
	{
		if(mini) {
			this.HideMenu();
			this.target_list = 'DropBox';
			this.UpdateIM();
			this.current_node = this.imnode_mini;
			this.ShowMenu(true);
		}
		else {
			this.HideMenu();
			this.target_list = 'Num';
			this.UpdateIM();
			this.current_node = this.imnode;
			this.ShowMenu(true);
		}
	},
	keydown_handler: function(obj,code)
	{
		// we need to use keydown for esc...
		if(!this.available) {
			var now = lib.time();

			switch(code) {
			case 27:
				if(this.escape_last > 0 && now - this.escape_last < 500) {
					this.escape_last = 0;
					this.ShowMenu();
				}
				else this.escape_last = now;
				break;
			default:
				return true;
				break;
			}
			return false;
		}
		else return true;
	},
	keypress_handler: function(obj,code)
	{
		if(this.available && this.ime.available) {
			switch(code) {
			case 104: // h - open help
				var url;

				if(GownFullStringTable.language) url = GownFullConfig.help_url + GownFullStringTable.language + '.html';
				else url = GownFullConfig.help_url;

				this.ime.browserwin.OpenURL(url);
				this.ime.browserwin.SetTitle($GFS('Help'));
				break;
			case 113: // q - toggle internal buffer
				if(!this.ime.internalwin.shown) this.ime.StartInternalBuffer(obj);
				else this.ime.EndInternalBuffer();
				break;
			default:
				return true;
				break;
			}
			this.HideMenu();
			return false;
		}
		else return true;
	}
});

var GenericGownFullBuilder = GownFullBuilder.extend({
	show: true,
	last_im: 0,
	config: null,
	node_adapter_prototype: null,

	constructor: function(config)
	{
		this.config = {};
		this.config.mainwin_id = GownFullConfig.mainwin_id;
		this.config.candwin_id = GownFullConfig.candwin_id;
		this.config.browserwin_id = GownFullConfig.browserwin_id;
		this.config.miniwin_id = GownFullConfig.miniwin_id;
		this.config.tooltipwin_id = GownFullConfig.tooltipwin_id;

		this.config.css_file = GownFullConfig.css_file;
		this.config.fixed = false;
		this.config.applyall = true;

		this.node_adapter_prototype = {};
		this.node_adapter_prototype['input'] = new GownFullHTMLInputTextAdapter;
		this.node_adapter_prototype['textarea'] = new GownFullHTMLTextareaAdapter;
		this.node_adapter_prototype['iframe'] = new GownFullHTMLRichEditAdapter;

		if(config) {
			for(var key in config) this.config[key] = config[key];
		}
		Debug.trace(0,'GownFullBuilder: Using GenericGownFullBuilder (fixed=%s).',this.config.fixed);
		this.base();
	},
	BuildCSS: function()
	{
		return lib.LoadCSSFile(this.config.css_file);
	},
	BuildMainWindow: function(ime)
	{
		if(this.config.fixed) return new GenericMainWindow(ime,this.config.mainwin_id,true);
		else return new GenericMainWindow(ime,this.config.mainwin_id);
	},
	BuildCandidateListWindow: function()
	{
		return new GenericCandidateListWindow(this.config.candwin_id);
	},
	BuildBrowserWindow: function()
	{
		return new GenericBrowserWindow(this.config.browserwin_id);
	},
	BuildTooltipWindow: function()
	{
		return new GenericTooltipWindow(this.config.tooltipwin_id);
	},
	BuildMiniBufferManager: function(ime)
	{
		return new GenericMiniBufferManager(ime,this.config.miniwin_id);
	},
	BuildMenu: function(ime)
	{
		return new GenericGownFullMenu(ime);
	},
	onKeyDown: function(e)
	{
		var key = e.which ? e.which : e.keyCode;
		var obj = lib.GetEventTarget(e);
		var b;
		var m = 0;

		Debug.trace(5,'keydown (key=%d,target=%s,m=%b)',key,obj,m);
		// check if the object has object adapter...
		if(!GownFullObjectAdapter.GetAdapter(obj)) this.CreateAdapter(obj);

		if(e.altKey) m |= Const.KEY_ALT;
		else if(e.ctrlKey) m |= Const.KEY_CTRL;
		else if(e.shiftKey) m |= Const.KEY_SHIFT;

		b = this.keyboard_handler(obj,Const.KEY_TYPE_DOWN,key,m);
		
		if(!b) {
			if(e.preventDefault) e.preventDefault();
		}
		return b;
	},
	onKeyUp: function(e)
	{
		var key = e.which ? e.which : e.keyCode;
		var obj = lib.GetEventTarget(e);
		var b;
		var m = 0;

		Debug.trace(5,'keyup (key=%d,target=%s,m=%b)',key,obj,m);
		// check if the object has object adapter...
		if(!GownFullObjectAdapter.GetAdapter(obj)) this.CreateAdapter(obj);

		if(e.altKey) m |= Const.KEY_ALT;
		else if(e.ctrlKey) m |= Const.KEY_CTRL;
		else if(e.shiftKey) m |= Const.KEY_SHIFT;

		b = this.keyboard_handler(obj,Const.KEY_TYPE_UP,key,m);

		if(!b) {
			if(e.preventDefault) e.preventDefault();
		}
		return b;
	},
	onKeyPress: function(e)
	{
		Debug.trace(7,'keypress (char=%d,key=%d,which=%d)',e.charCode,e.keyCode,e.which);
		// if we only have keyCode but no charCode and which and WE ARE NOT IN IE, then it may not be a character, skip it
		if(!GownFullBrowserDetect.bad_keypress && e.keyCode && !e.which && !e.charCode) return true;

		var key = e.which ? e.which : e.keyCode;
		var obj = lib.GetEventTarget(e);
		var b;
		var m = 0;

		Debug.trace(5,'keypress (key=%d,target=%s,m=%b)',key,obj,m);
		// check if the object has object adapter...
		if(!GownFullObjectAdapter.GetAdapter(obj)) this.CreateAdapter(obj);

		if(e.altKey) m |= Const.KEY_ALT;
		else if(e.ctrlKey) m |= Const.KEY_CTRL;
		else if(e.shiftKey) m |= Const.KEY_SHIFT;

		b = this.keyboard_handler(obj,Const.KEY_TYPE_PRESS,key,m);

		if(!b) {
			if(e.preventDefault) e.preventDefault();
		}
		return b;
	},
	onFocus: function(e)
	{
		var obj = lib.GetEventTarget(e);

		this.onfocus_handler(obj);
		return true;
	},
	onLostSelection: function(e)
	{
		var obj = lib.GetEventTarget(e);

		this.lostsel_handler(obj);
		return true;
	},
	CreateAdapter: function(obj)
	{
		// check if the object has adapter...
		if(GownFullObjectAdapter.GetAdapter(obj)) return true;

		var nodeName = obj.nodeName;
		var pt;
		var adapter;

		if(!nodeName) {
			Debug.trace(0,'GenericGownFullBuilder: object has no nodeName.');
			return null;
		}
		
		nodeName = nodeName.toLowerCase();

		pt = this.node_adapter_prototype[nodeName];
		if(!pt) {
			Debug.trace(0,'GenericGownFullBuilder: No adapter prototype for nodeName = %s',nodeName);
			return null;
		}
		adapter = pt.clone(obj);
/*		var x = this;

		adapter.AttachHandler(
			function(e) { return x.onKeyDown(e); },
			function(e) { return x.onKeyPress(e); },
			function(e) { return x.onKeyUp(e); },
			function(e) { return x.onFocus(e); },
			function(e) { return x.onLostSelection(e); }
		);
*/
		return adapter;
	},
	AttachHandler: function(obj,keyp)
	{
		var adapter;
		var x = this;

		adapter = this.CreateAdapter(obj);
		if(keyp) {
			adapter.AttachHandler(
				function(e) { return x.onKeyDown(e); },
				function(e) { return x.onKeyPress(e); },
				function(e) { return x.onKeyUp(e); },
				function(e) { return x.onFocus(e); },
				function(e) { return x.onLostSelection(e); }
			);
			return true;
		}
		else {
			adapter.AttachHandler(null,null,null,
				function(e) { return x.onFocus(e); },
				function(e) { return x.onLostSelection(e); }
			);
			return true;
		}
	},
	ApplyInputMethodByTagName: function(name,keyp,filter)
	{
		var nodes,c,i;

		nodes = document.getElementsByTagName(name);
		c = 0;
		for(i=0;i<nodes.length;i++) {
			if(!filter || filter(nodes.item(i))) {
				if(this.AttachHandler(nodes.item(i),keyp)) c++;
			}
		}
		Debug.trace(0,"GenericGownFullBuilder: Input Method attached for HTML tag %s, %d attached.",name,c);
		return c;
	},
	ApplyInputMethodByFrameAndTagName: function(target_frame,name,keyp,filter)
	{
		var nodes,c,i;

		nodes = target_frame.document.getElementsByTagName(name);
		c = 0;
		for(i=0;i<nodes.length;i++) {
			if(!filter || filter(nodes.item(i))) {
				if(this.AttachHandler(nodes.item(i),keyp)) c++;
			}
		}
		Debug.trace(0,"GenericGownFullBuilder: Input Method attached for HTML tag %s, %d attached at frame (%s).",name,c,target_frame.name);
		return c;
	},
	RApplyInputMethod: function(target_frame,name,keyp,filter)
	{
		var i,fre;

		try {
			this.ApplyInputMethodByFrameAndTagName(target_frame,name,keyp,filter);
		}
		catch(e) {
			Debug.trace(0,'GenericGownFullBuilder: RApplyInputMethod, exception (%s)',e);
		}
			/*
var frs = document.getElementsByTagName('frame');
var i;

for(i=0;i<frs.length;i++) {
  var frm = frs.item(i);
  alert(frm.name);
}
*/
		// Special handle for IE
		if(GownFullBrowserDetect.current_browser == GownFullBrowserDetect.BROWSER_IE) {
			var frs = target_frame.document.getElementsByTagName('frame');
			var frm_name;

			for(i=0;i<frs.length;i++) {
				frm_name = frs.item(i);
				frm_name = frm_name.name;
				fre = target_frame.frames[frm_name];
				try {
					this.RApplyInputMethod(fre,name,keyp,filter);
				}
				catch(e) {
					Debug.trace(0,'GenericGownFullBuilder (IE): RApplyInputMethod, exception (%s)',e);
				}
			}
		}
		else {
			for(i=0;i<target_frame.frames.length;i++) {
				fre = target_frame.frames[i];
	
				try {
					this.RApplyInputMethod(fre,name,keyp,filter);
				}
				catch(e) {
					Debug.trace(0,'GenericGownFullBuilder: RApplyInputMethod, exception (%s)',e);
				}
			}
		}
	},
	isIFrameRichEdit: function(obj)
	{
		var doc;
		var isRichEdit = false;

		try {
			doc = obj.contentWindow.document;
			var str = doc.designMode;
			str = str.toLowerCase();
			if(str == 'on') isRichEdit = true;
		}
		catch(e) {
			Debug.trace(0,"GenericGownFullBuilder: isIFrameRichEdit, exception: %s",e);
		}
		try {
			doc = obj.contentWindow.document;
			var body = doc.body;
			if(doc.body.contentEditable) isRichEdit = true;
		}
		catch(e) {
			Debug.trace(0,"GenericGownFullBuilder: isIFrameRichEdit, exception: %s",e);
		}
		return isRichEdit;
	},
	PostCreateGownFull: function()
	{
		var x = this;

		if(this.config.applyall) {
			this.RApplyInputMethod(self,'input',false,function(obj) { return (obj.type && obj.type == 'text') ? true : false; });
			this.RApplyInputMethod(self,'textarea',false);
			this.RApplyInputMethod(self,'iframe', true,function(obj) { return x.isIFrameRichEdit(obj); });
			// attach a "global" onkey* event... 
			// (dynamically added TEXTAREA and INPUT text can now handle, but not RichEdit...)
			// Picasa Web Album should work now....
			if(GownFullBrowserDetect.use_key_addEventListener) {
				// I found that EditGrid will call cancelBubble...use we need to useCapture...
				document.addEventListener("keydown", function(e) { return x.onKeyDown(e); },true);
				document.addEventListener("keypress", function(e) { return x.onKeyPress(e); },true);
				document.addEventListener("keyup", function(e) { return x.onKeyUp(e); },true);
			}
			else if(GownFullBrowserDetect.use_attachEvent) {
				document.attachEvent("onkeydown", function(e) { return x.onKeyDown(e); });
				document.attachEvent("onkeypress", function(e) { return x.onKeyPress(e); });
				document.attachEvent("onkeyup", function(e) { return x.onKeyUp(e); });
			}
			else {
				Debug.trace(0,'GenericGownFullBuilder: WARNING, builder is using traditional model!');
				this.old_onkeydown = document.onkeydown;
				this.old_onkeypress = document.onkeypress;
				this.old_onkeyup = document.onkeyup;
	
	
				document.onkeydown = function() {
					var b = true;
					if(x.old_onkeydown) b &= x.old_onkeydown();
					b &= x.onKeyDown(window.event);
					return b;
				}
				document.onkeypress = function() {
					var b = true;
					if(x.old_onkeypress) b &= x.old_onkeypress();
					b &= x.onKeyPress(window.event);
					return b;
				}
				document.onkeyup = function() {
					var b = true;
					if(x.old_onkeyup) b &= x.old_onkeyup();
					b &= x.onKeyUp(window.event);
					return b;
				}
			}
// FIXME: How to attach handler to a "dynamically" created RichEdit without DOMSubtreeModified event?!
// no browsers support this event: http://www.quirksmode.org/js/events_compinfo.html
// DOM Level 2 Events spec: http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html#Events-MutationEvent
// need this to make GMail work.
		}
	}
});
